{
	"id": 49226,
	"body": "
draft document -- 2003年6月11日

\n
作为一名web开发者，任何时候当你构建一个Web应用时，有责任确保你的应用程序能够做什么和什么是应该做的：在发生错误的时候优雅的处理错误；让用户获取不到他们不应该查看的数据；防止恶意用户对应用程序进行的干扰操作。

\n
然而，详细的讨论如何去构建一个稳定的web应用程序是一个太过复杂的主题，这一篇短文设计的是构建基于Velocity的web应用程序常见的几个问题。短文是从一个开发者的角度写起的，他需要同大量的html模板开发人员和最终用户接触。我们鼓励读者发表自己的意见，其他问题和相关的设计建议请致信 Velocity 用户列表，或者直接联系作者，Will Glass-Husain。

\n
Velocity 如何帮助开发者创建一个稳定的App

\n
通常，Velocity 是一个html设计师容易学会，并且不会难用的web模板工具。这一点基于如下的几个要素：

\n
Velocity 模板语言（VTL）是简单的。 使用几个简单的指令，外加应用本身定义的引用，需要学习的东西少（也不怎么凌乱）。

\n
页面文件中不存储非展示用的信息。 对比JSP文件，它常常需要带上一个页面头文件。如果一个不懂技术的web设计师去掉了这个头部，那么这个JSP页面就不起作用了。在一个Velocity模板中这样的头部或者其他技术信息是不需要的。

\n
在一个web页面中没有Java代码。 这样就消除了一些共有的错误和混乱设计，同时让恶意的页面编辑者去调用不必要的Java代码变得困难。

\n
一个方法的异常不会阻塞一个页面的创建。 使用MethodExceptionEventHandler，一个方法异常可以被获取和记录日志。

\n
一个无效的引用不会阻塞一个页面的创建。 模板指令中无效的引用通常被忽略不计。页面中一个无效的引用将会简单的把其引用参考字打印出来。（比如：$foo）。

\n
基于如上的原因，大部分Velocity开发者将会发现：没有什么离奇的现象或者问题，页面以一种直接的方式被渲染。

\n

\n
基于Velocity稳定和安全性的具体问题

\n
考虑安全和稳定要素时，你需要意识到基于Velocity的web应用程序的几个特性。

\n
Velocity 是一个模板工具， 而不是一个框架。 它不解决任何像认证、访问控制、会话状态或者数据持久化这样的问题。

\n
VTL方法调用实际上是Java的方法调用。 这意味着一个糟糕的velocity应用程序设计使得模板设计者改变系统的状态，直接执行SQL查询或者随意的实例化Java类。潜在的安全威胁将在下面被详细提到。

\n
VTL引用具有Java类型。尽管对被模板编辑者是不可见的，每一个引用还是一个具有特定类型的Java对象。如果 $apple 是一个 integer 的 “1”， $orange是一个String 的“1”，$banana是一个 double 的 “1.0”，那么根据VTL这些对象没有一个是==（对等）的。典型的非技术html模板设计者可能会对此感到迷惑。（事实上，如果对象不是同一个类型的，会有对它们字符串值的比较。因此， 在VTL中， $apple和 $orange现在是对等的了。）

\n

\n
在构建安全，稳定的Velocity Web应用程序中的最佳实践。

\n
如下所列是能够帮助你构建一个稳定的Velocity Web应用程序的最佳实践。它们包括：

\n
\n
在上下文环境context中审查所有不必要的方法。

\n
\n
\n
编码HTML特殊字符，以避免交叉脚本漏洞。（cross-scripting）

\n
\n
\n
使用最新且做了合适设置的app服务器。

\n
\n
\n
进行适合生产中使用的Velocity配置

\n
\n
在上下文环境context中审查所有不必要的方法。

\n
开发者放在上下文环境中的引用一般有一到两个重要的目的。

\n
1、提供在页面上面显示的动态信息（比如：当前用户的名字）。

\n
2、提供辅助的控制结构和信息的重新格式化（比如：格式化数字的工具）。

\n
把存在的对象或者bean放入上下文环境这样的做法是很有诱惑力的。当你这样做的时候，你必须意识到一个很重的警告。模板可以调用对象在页面上下文环境中可用的任何公共方法。这意味着你应该小心地，只提供能安全的被模板设计者访问的方法。

\n
为了隐藏不必要的方法，开发者需要创建一个包装对象。需要重点注意的是，成为一个对象的子对象或者实现一个接口以隐藏方法做得还不够。原因是：

\n
使用接口对于模板设计者访问实现了这个接口的这个类的任何公共方法没有施加任何影响。

\n
      当模版设计者能够使用 VTL 指令 $reference.super().badmethod() 调用一个来自父类的方法时，采用子类的方式没有帮助。

\n
有一些需要特别关注的事情：

\n
      不要包含那种拥有可以改变应用状态的任何方法的对象。那样做的话会破坏 MVC 模式，而且很难去调试。

\n
      避免拥有可以执行SQL查询的方法的对象/关系型数据库对象。Jakarta 项目 Torque 和 Turbine的用户需要特别注意的是：生成的Torque对象包含了一个可以访问org.apache.Torque.util.BasePeer 类的一个实体的getPeer方法。这个类包含了许多允许随意执行SQL查询的方法。

\n
      永远不要把文件和类似的对象放到上下文环境中。永远通过封装的对象展示文件系统信息。

\n
编码HTML特殊字符，以避免交叉脚本漏洞。

\n
任何时候一个web应用程序要显示之前用户输入的文本，都存在一个包含非法文本的风险。通常，这样的文本可能包含将导致页面表现超出作者预期行为的，使用了Javascript 的 HTML 标签。当页面被第二个人看到的时候，这是一个问题：被包含的文本会弹开窗口；抓取cookie信息；或者拦截输入到表单里面的数据。正因为这是一个普遍的web应用程序设计问题，网上才会查到大量的有关这个潜在危险的更多信息。

\n
解决办法是：在屏幕上显示它们之前，一直避免使用HTML特殊字符。至少，作如下的替换：

\n
 

\n
原文文本                 替换文本

\n
<                       <

\n
>                       >

\n
"
	"

\n
&                       &

\n
 

\n
为了更好的可读性，也要替换这些：

\n
原文文本                 替换文本

\n
回车符                  <br/>

\n
两个空格                 一个空格跟着&nbsp;

\n
 

\n
有四种可能被用到的替换文本的方法

\n
1、你可以在处理用户输入时回避掉这样的文本，使用编码的形式存储它们。这对于模板设计者来说是最简单的，但是会在当你需要在其他场景下使用没有回避的文本形式时引发问题。

\n
2、你可以创建一个在显示时回避文本的工具。（例 1，如下）

\n
3、你可以为上下文环境的所有文本创建一个封装的对象，自动回避这样的字符串。（例 2）。

\n
4、如 Danil Dekany 建议的那样，Velocity 可以包含一个新的，可以在块内自动回避文本的指令#escape。

\n


\n
 

\n
例 1：Velocity 回避文本的工具。

\n
$HTMLText.setText($textFromUser).Escaped

\n
$HTMLText.setText($textFromUser).EscapedMultiLine

\n
 

\n
第一行所有的 HTML 字符都会被回避掉， 显示 $textFromUser 的原始文本。第二将把任何传入的返回转换成 <br/> 表现形式。（注意：你将不会想在所有情况下都这样做。比如：你应该包含作为在一个表单textarea域中Enter值的传入返回值。

\n
 

\n
例 2：使用一个封装的文本处理对象。

\n
$textFromUser.Escaped

\n
$textFromUser.EscapedMultiLine

\n
$textFromUser.PlainText

\n
 

\n
在这个例子中， 上下文环境中的 $textFromUser 被定义成了一个拥有 Escaped，EscapedMultiLine 和 PlainText 属性的封装对象。第一个属性显示 HTML 代码被避免的文本。第二个也编码的传入返回。最后一个属性返回跟输入时一样的文本。如果三个属性一个都没有给出，默认显示的文本应该被避免掉。

\n
 

\n
例 3：使用 #escape 指令（没有被实现)。

\n
#escape("
	html ")

\n
  ... Tons of HTML here...

\n
  ... interpolations will be implicitly escaped

\n
#end

\n

\n
使用最新且做了合适设置的app服务器。

\n
对于任何web应用程序，确保你基于Velocity的app是运行于使用最近更新或者服务包的应用服务器上。研究研究跟安全相关的配置设置，跟踪已经发布的漏洞。作者遇到的一些问题包括：

\n
配置你的应用服务器，使用 -Xmx java 命令行选项使Java虚拟机允许一个足够的最大栈空间。如果没有这个设置，你有的应用很有可能抛出 OutOfMemoryError 。

\n
      当使用一个web服务器和Apache Tomcat 时，你经常必须明确禁止用户访问 WEB-INF， META-INF，和其他系统路径。

\n
      上溯至版本 4.1.12 版本的 Apache Tomcat 应该欺骗性的使用 DefaultServlet 来显示 Velocity 页面， JSP，或者任何其他web树结构中的模板，它们的源代码。

\n
      配置一个 Java Security Manager 限制文件的访问（从web 树和模板路径的外部）和危险的方法，如 System.exit()和 getClassLoader 的访问。这已经被技术性的包含在了现当前版本的Velocity中（1.3.1），没有被完全支持。尤其是，一些Velocity类需要访问Classloader，而其它应该被严格限制。如果你对于这些问题不是很熟悉，Velocity用户列表也许能帮助你着手开始。

\n
只有一个基本的准则——小心关注你的特有系统的问题。

\n

\n
进行适合生产中使用的Velocity配置

\n
一个安装在生产服务器上的web应用程序比开发环境下，通常在配置上有很大的不同。确保利用Velocity的扩展性的 develope-guide.html#Velocity配置Keys和Values配置选项，去创建一个稳定和专业的部署。

\n
下面是一些建议：

\n
创建一个 EventCartridge 和 Event Handler 去获取方法异常。记录异常日志，但是运行页面继续进行处理。

\n
      打开Velocity页面的缓存，除了能加速页面渲染的处理外，这样也避免了在必然的情况下由于过度的也页面内调导致的“内存溢出”。

\n
      创建同意的错误页面，以同意你的应用的观感。在获取到 ParseErrorException 和 ResourceNotFoundException 时显示这个错误页面（由 Velocity.getTemplate()抛出）。包含一个对用户友好的错误消息（以Velocity引用的形式传给页面），并且为 开发者和系统管理员记录技术性的详细日志。

\n

\n

\n
同不受信任的HTML 模板设计者一起工作

\n
（注意：下面一些特定的引用代码是过时的。见版本1.5 WGH的增补清单 - 2005/10/7)

\n
许多Velocity应用程序，一小组人（或者仅仅一个人）为开发一个web应用而协同工作。这种情况下，开发者主要关注的是为最终用户创建一个用户友好的安全应用。开发者为页面设计提供一些简单的技术指导，大部分有尤其是一个VTL引用工具清单，和一些访问web树的CVS和FTP设置。在这个普遍的场景中，开发者和模板编辑者都有确保让应用安全平稳的进行工作的责任。

\n
其他的Veloctiy应用，一大群模板设计者创建模板文件，也许是来自外部的开发组织。常常，这些模板设计者没有直接访问CVS树或者web文件系统。而可能是通过一个web管理界面上传他们为这个web应用上传的模板文件。这在中情况下，模板编辑者应该被看做是不受信任的。不管是什么模板被上传到了系统里面，都需要特别关注web应用程序的集成。

\n
然而不完全清楚到底有多少Velocity Web 应用程序是属于这个范畴的，作者遇到过五六个创建过这种类型应用程序的开发者。作者自己也管理这一个基于Velocity的web站点，里面有上百个HTML模板设计者，他们拥有具有上传模板能力的账号。

\n
一些在同不受信任的模板设计者一起开发一个web应用程序时的注意事项：

\n
如上所述，在上下文环境中只提供安全的引用。方法不能改变app的状态、执行SQL查询，或者访问文件系统。

\n
      复审 #include 和 #parse 的潜在使用。在作者的应用程序中，模板文件盒私有的用户数据起初被存储在同一个资源路径下的web树的平行目录中。这意味着任何模板用户能使用来自另外一个用户的模板和数据。Serge Knystautas 提出了一个针对这个困境的可能的解决方案，它是去创建一个跟用户相关资源加载器，加载属于当前用户的模板文件。另外的一个解决办法是使用一个作者开发Velocity的包，它允许开发者通过使用事件Handler控制实际由 #include 和 #parse 返回的模板。（典型的，这将限制每个账户只包含那个账户中的页面）。（更新：新的事件Handler包含在源代码树中，发布于 版本 1.5 - WGH中）

\n
此外，上面关于配置一个security manager的话题在这种类型的应用中变得很重要。开发者应该意识到模板设计者具有调用 getClassLoader() 返回一个能够在默认配置中实例化任何类和调用任何方法的 ClassLoader。作者已经为Velocity推出了一个限制这种危险能力的包。（更新：计划于版本 1.6 - WGH 中）

\n
明确的最安全的途径是限制模板设计在一小组受信任的模板编辑者中。然而，在有一大群用户可以上传模板的情况下，Velocity仍然是一个有用的工具。这种情况下，你必须更加小心的考虑围绕系统集成和安全衍生的问题。

\n
 

\n
鸣谢

\n
作者很感谢velocity用户列表中的许多为这些相关问题提供过建议，特别是那些加入了这个 discussion thread 中的成员。

\n
----------------------------------------

\n
这里是原文的地址：

\n
http://wiki.apache.org/velocity/BuildingSecureWebApplications

",
	"pubDate": "2013-05-23 10:56:47.0",
	"author": "廖凯",
	"title": " [翻译]使用 Velocity 构建一个稳定安全的Web应用",
	"authorid": 146430,
	"relativies": [{
		"title": "velocity的基本使用模式",
		"url": "http://home.oschina.net/chen106106/blog/40580"
	}, {
		"title": "Velocity 文档(1)",
		"url": "http://home.oschina.net/aiguozhe/blog/39553"
	}, {
		"title": "Velocity文档（2）",
		"url": "http://home.oschina.net/aiguozhe/blog/39556"
	}, {
		"title": "[转帖]Velocity的layout功能",
		"url": "http://home.oschina.net/cool2010/blog/35131"
	}, {
		"title": "【转】一篇Velocity入门级的文章",
		"url": "http://home.oschina.net/aiguozhe/blog/39512"
	}],
	"notice": {
		"replyCount": 0,
		"msgCount": 0,
		"fansCount": 0,
		"referCount": 0
	},
	"favorite": 0,
	"commentCount": 3,
	"url": "http://home.oschina.net/u/146430/blog/49226"
}